//
//  String+Extension.swift
//  YangFoundation
//
//  Created by yuyang on 2021/12/21.
//

import UIKit
import CommonCrypto

public extension String {

    /// 返回字符串长度
    var length: Int { return self.count }
    
    /// 字符串中是否为浮点数
    func isPureFloat() -> Bool {
        let scan: Scanner = Scanner(string: self)
        var val:Float = 0.0
        return scan.scanFloat(&val) && scan.isAtEnd
    }

    /// 字符串中是否为整数
    func isPureInt() -> Bool {
        let scan: Scanner = Scanner(string: self)
        var val:Int = 0
        return scan.scanInt(&val) && scan.isAtEnd
    }
    
    /// 字符串中是否包含中文
    func isIncludeChinese() -> Bool {
        for value in self {
            if ("\u{4E00}" <= value  && value <= "\u{9FA5}") {
                return true
            }
        }
        return false
    }
    
    /// 手机号加密（中间四位密文）
    var mobileEncrypt: String {
        if self.count >= 11 {
            let begin = self.index(self.startIndex, offsetBy: 3)
            let end = self.index(begin, offsetBy: 4)
            let range = begin..<end
            var str = self
            str.replaceSubrange(range, with: "****")
            str.insert(" ", at: end)
            str.insert(" ", at: begin)
            return str
        }
        return self
    }

    /// 字符串转字典
    func toDictionary() -> [String: Any] {
        var result = [String: Any]()
        guard !self.isEmpty else { return result }
        guard let dataSelf = self.data(using: .utf8) else {
            return result
        }
        if let dic = try? JSONSerialization.jsonObject(with: dataSelf, options: .mutableContainers) as? [String: Any] {
            result = dic
        }
        return result
    }
}

// MARK: - 富文本 Range & NSRange
public extension String {
    
    /// 富文本支持
    /// - Parameters:
    ///   - fullText: 全部文案
    ///   - markText: 特殊文案
    ///   - fullTextAttributes: 全部文案的Attributes
    ///   - markTextAttributes: 特殊文案的Attributes
    /// - Returns: NSMutableAttributedString
    static func attributedString(fullText: String, markText: String, fullTextAttributes: [NSAttributedString.Key : Any] = [:], markTextAttributes: [NSAttributedString.Key : Any] = [:]) -> NSMutableAttributedString {
        let attributedText = NSMutableAttributedString(string: fullText)
        if let range = fullText.range(of: markText) {
            let nsrange = fullText.toNSRange(range)
            attributedText.addAttributes(fullTextAttributes, range: NSRange(location: 0, length: attributedText.length))
            attributedText.addAttributes(markTextAttributes, range: nsrange)
        }
        return attributedText
    }
    
    /// Range转NSRange
    func toNSRange(_ range: Range<String.Index>) -> NSRange {
        guard let from = range.lowerBound.samePosition(in: utf16), let to = range.upperBound.samePosition(in: utf16) else {
            return NSRange(location: 0, length: 0)
        }
        return NSRange(location: utf16.distance(from: utf16.startIndex, to: from), length: utf16.distance(from: from, to: to))
    }
    
    /// NSRange转Range
    func toRange(_ range: NSRange) -> Range<String.Index>? {
        guard let from16 = utf16.index(utf16.startIndex, offsetBy: range.location, limitedBy: utf16.endIndex) else { return nil }
        guard let to16 = utf16.index(from16, offsetBy: range.length, limitedBy: utf16.endIndex) else { return nil }
        guard let from = String.Index(from16, within: self) else { return nil }
        guard let to = String.Index(to16, within: self) else { return nil }
        return from ..< to
    }
}

// MARK: - Encode & Decode
public extension String {
    var base64Encoding: String {
        let data = self.data(using: .utf8)
        return data?.base64EncodedString(options: .init(rawValue: 0)) ?? ""
    }

    var base64Decoded: String {
        let data = Data.init(base64Encoded: self, options: .init(rawValue: 0)) ?? Data()
        return String.init(data: data, encoding: .utf8) ?? ""
    }

    func md5() -> String {
        let str = cString(using: .utf8)
        var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
        CC_MD5(str, CC_LONG(str!.count-1), &digest)
        return digest.reduce("") { $0 + String(format: "%02x", $1) }
    }

    func sha1() -> String {
        // UnsafeRawPointer
        let data = self.data(using: String.Encoding.utf8)!
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
        let newData = NSData.init(data: data)
        CC_SHA1(newData.bytes, CC_LONG(data.count), &digest)
        let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH * 2))
        for byte in digest {
            output.appendFormat("%02x", byte)
        }
        return output as String
    }
}
